#pragma once
#include "dvo.h"
#include <iostream>

class Command {
public:
  Command(std::string& str) : str_(str){}
  virtual void Execute()  { std::cout << str_.c_str() << std::endl;}
private:
  std::string str_;
};
class Result{
public:
  Result(std::string& str) : str_(str){}
private:
  std::string str_;
};

class Request{
public:
  Request(std::string& str) : str_(str){}
private:
  std::string str_;
};

enum selector_outputs_t { LogOutput, ReqOutput, CmdOutput};
class Selector : public dvo::ps::Selector<selector_outputs_t, std::string, std::string>{
public:
  Selector() {Add(LogOutput); Add(ReqOutput); Add(CmdOutput); }
protected:
  virtual void Process(std::string& input) override{
    std::string type_id = input.substr(0,3);
    input = input.substr(3);
    if(type_id.compare("cmd") == 0){
      Send(selector_outputs_t::CmdOutput, input);
    }else if(type_id.compare("req") == 0){
      Send(selector_outputs_t::ReqOutput, input);
    }else if(type_id.compare("log") == 0){
      Send(selector_outputs_t::LogOutput, input);
    }
  }
};

bool test_msg_handler(){
  return true;
}

void run_msg_handler(){
  Selector selector;
  dvo::ps::SimpleTransceiver<std::string, Command> cmdconverter = [](std::string& str) { Command cmd(str); return cmd; };
  dvo::ps::SimpleTransceiver<std::string, Request> reqconverter = [](std::string& str) { return Request(str);};
  dvo::ps::SimpleTransceiver<std::string, Result> resconverter = [](std::string& str) { return Result(str);};
  dvo::ps::SimpleReceiver<Command> executor = [](Command& cmd) {cmd.Execute();};
  dvo::ps::SimpleReceiver<Result> logger = [](Result& res) { std::cout << "Save a log" << std::endl; };
  dvo::ps::SimpleTransceiver<Request, std::string> provider = [](Request& req) { std::cout << "fulfill a request" << std::endl; return std::string("Ack"); };
  dvo::ps::SimpleReceiver<std::string> sender = [](std::string& msg) { std::cout << "sending message: " << msg.c_str() <<  std::endl; };
  
  *selector.Outputs(LogOutput) >> resconverter.Input();
  *selector.Outputs(ReqOutput) >> reqconverter.Input();
  *selector.Outputs(CmdOutput) >> cmdconverter.Input();

  *cmdconverter.Output() >> executor.Input();
  *reqconverter.Output() >> provider.Input();
  *resconverter.Output() >> logger.Input();
  
  *provider.Output() >> sender.Input();

  std::vector<std::string> msgs;
  msgs.push_back("cmdThis is a command");
  msgs.push_back("resThis is a result");
  msgs.push_back("reqThis is a request");
  
  for(auto& msg : msgs) msg >> *selector.Input();
}